﻿using System;
using System.Collections.Generic;
using UnityEngine;



public class Grass : MonoBehaviour
{

    public static Grass instnace;
    
    [SerializeField]
    public int grassCnt = 10000;
    
    [SerializeField]
    public Material instanceMaterial;

    public ComputeShader cullingComputeShader;

    public float grassDrawDistance = 200;
    
        
        
    
    private int m_grassCnt = 0;
    private readonly List<Vector3> m_grassPosList = new List<Vector3>();
    
    
    private ComputeBuffer m_grassAllPosBuffer = null;
    // 草的idbuff
    private ComputeBuffer m_grassVisibleIDBuffer = null;
    
    private ComputeBuffer m_grassRenderArgsBuffer = null;

    private Mesh m_grassMesh;
    private Bounds m_renderBounds;

    private float m_grassBoundsXMin, m_grassBoundsZMin, m_grassBoundsZMax, m_grassBoundsXMax;


    private readonly float m_cellSizeX = 5;
    private readonly float m_cellSizeZ = 5;

    private int m_cellCountX,m_cellCountZ;
    private List<Vector3>[] m_cellToPosList;
    
    private List<int> m_visibleCellIdList = new List<int>();
    
    private Plane[] m_cameraFrustumPlanes = new Plane[6];


    private void OnEnable()
    {
        instnace = this;
    }

    private void Start()
    {
        m_grassMesh = new Mesh();
        var verts = new Vector3[3];
        verts[0] = new Vector3(-0.25f /4 , 0f);
        verts[1] = new Vector3(0.25f / 4, 0f);
        verts[2] = new Vector3(0f, 1f / 4);
        int[] trinagles = new int[3]{2,1,0};
        m_grassMesh.SetVertices(verts);
        m_grassMesh.SetTriangles(trinagles, 0);
        
        uint[] args = new uint[5]{0,0,0,0,0};
        m_grassRenderArgsBuffer = new ComputeBuffer(1, args.Length * sizeof(uint), ComputeBufferType.IndirectArguments);
        args[0] = (uint) m_grassMesh.GetIndexCount(0);
        args[1] = (uint) grassCnt;
        args[2] = (uint) m_grassMesh.GetIndexStart(0);
        args[3] = (uint) m_grassMesh.GetBaseVertex(0);
        m_grassRenderArgsBuffer.SetData(args);

    }
    
    

    private void LateUpdate()
    {
        
        
        this.UpdateGrassPosList();
        
        // 更新computeshader
        m_visibleCellIdList.Clear();
        var camera = Camera.main;
        
        // 计算当前摄像机视野内的草所在区域可见的id
        float oriFarPlane = camera.farClipPlane;
        camera.farClipPlane = grassDrawDistance;
        GeometryUtility.CalculateFrustumPlanes(camera, m_cameraFrustumPlanes);
        camera.farClipPlane = oriFarPlane;

        
        
        Vector3 centerPos = new Vector3();
        Bounds cellBounds = new Bounds();
        cellBounds.size = new Vector3(Mathf.Abs((m_grassBoundsXMax - m_grassBoundsXMin)/ m_cellCountX), 0, Math.Abs(m_grassBoundsZMax - m_grassBoundsZMin)/m_cellCountZ); 
        for (var i = 0; i < m_cellToPosList.Length; i++)
        {
            if (m_cellToPosList.Length > 0)
            {
                var x = i % m_cellCountX + 0.5f;
                var z = i / m_cellCountX + 0.5f;
                centerPos.x = Mathf.Lerp(m_grassBoundsXMin, m_grassBoundsXMax, x / m_cellCountX);
                centerPos.z = Mathf.Lerp(m_grassBoundsZMin, m_grassBoundsZMax,z/ m_cellCountZ);
                cellBounds.center = centerPos;
                if (GeometryUtility.TestPlanesAABB(m_cameraFrustumPlanes, cellBounds))
                {
                    m_visibleCellIdList.Add(i);
                }
               
            }
           
        }
        // 使用compute 计算可见格子里的cell 的草是否可见
        var v = camera.worldToCameraMatrix;
        var p = camera.projectionMatrix;
        var vp = p * v;
        // 清空buffer
        m_grassVisibleIDBuffer.SetCounterValue(0);
        
        cullingComputeShader.SetMatrix("_VPMatrix", vp);
        cullingComputeShader.SetFloat("_MaxDistance", grassDrawDistance);


        
        for (var i = 0; i < m_visibleCellIdList.Count; i++)
        {
            var cellId = m_visibleCellIdList[i];
            // 计算偏移起点
            int memoryOffset = 0;
            for (var j = 0; j < cellId; j++)
            {
                memoryOffset += m_cellToPosList[j].Count;
            }
        
            cullingComputeShader.SetInt("_StartOffset", memoryOffset);
            var checkCnt = m_cellToPosList[cellId].Count;
            
            cullingComputeShader.Dispatch(0, Mathf.CeilToInt(checkCnt / 64f),1,1);
        }

      

        // 获取当前草地的个数
        ComputeBuffer.CopyCount(m_grassVisibleIDBuffer, m_grassRenderArgsBuffer, 4);
        // 绘制草地
        Graphics.DrawMeshInstancedIndirect(m_grassMesh, 0, instanceMaterial, m_renderBounds, m_grassRenderArgsBuffer);
    }


    private void OnDisable()
    {

        if (m_grassRenderArgsBuffer != null)
        {
            m_grassRenderArgsBuffer.Release();
        }

        if (m_grassAllPosBuffer != null)
        {
            m_grassAllPosBuffer.Release();
        }

        if (m_grassVisibleIDBuffer != null)
        {
            m_grassVisibleIDBuffer.Release();
        }
    }


    void UpdateGrassPosList()
    {
        if (m_grassCnt == grassCnt)
        {
            return;
        }
        
        instanceMaterial.SetVector("_PivotPosWS",transform.position);
        instanceMaterial.SetVector("_BoundSize",new Vector2(transform.localScale.x, transform.localScale.z));
        
        m_grassCnt = grassCnt;
        

        if (m_grassAllPosBuffer != null)
        {
            m_grassAllPosBuffer.Release();
        }
        
        m_grassAllPosBuffer = new ComputeBuffer(m_grassCnt, sizeof(float) * 3);
        

        if (m_grassVisibleIDBuffer != null)
        {
            m_grassVisibleIDBuffer.Release();
        }
        m_grassVisibleIDBuffer = new ComputeBuffer(m_grassCnt, sizeof(uint), ComputeBufferType.Append);
        
        
        

        // 更新草的位置
        m_grassPosList.Clear();
        var centerPos = transform.position;
        
        var sizeX = transform.localScale.x / 2;
        var sizeZ = transform.localScale.z / 2;
        
        m_renderBounds.SetMinMax(new Vector3(centerPos.x - sizeX, 0, centerPos.x - sizeZ), new Vector3(centerPos.x + sizeX, 0, centerPos.z + sizeZ));

        m_grassBoundsZMin = m_grassBoundsXMin = float.MaxValue;
        m_grassBoundsXMax = m_grassBoundsZMax = float.MinValue;
        
        
        for (var i = 0; i < grassCnt; i ++) {
            var eachPos = new Vector3(
                centerPos.x + UnityEngine.Random.Range(-1f,1f) * sizeX,
                0,
                centerPos.z + UnityEngine.Random.Range(-1f,1f) * sizeZ
                );
            m_grassPosList.Add(eachPos);
            m_grassBoundsXMax = Mathf.Max(m_grassBoundsXMax, eachPos.x);
            m_grassBoundsXMin = Mathf.Min(m_grassBoundsXMin, eachPos.x);
            m_grassBoundsZMax = Mathf.Max(m_grassBoundsZMax, eachPos.z);
            m_grassBoundsZMin = Mathf.Min(m_grassBoundsZMin, eachPos.z);
        }
        // 根据草所在的区域重新绘制单元大小
        m_cellCountX = Mathf.CeilToInt((m_grassBoundsXMax - m_grassBoundsXMin) / m_cellSizeX);
        m_cellCountZ = Mathf.CeilToInt((m_grassBoundsZMax - m_grassBoundsZMin) / m_cellSizeZ);
        
        m_cellToPosList = new List<Vector3>[m_cellCountX * m_cellCountZ];
        for (var i = 0; i < m_cellToPosList.Length; i++) {
            m_cellToPosList[i] = new List<Vector3>();;   
        }
        // 将每个草的位置放到对应单元内
        for (var i = 0; i < m_grassCnt; i++)
        {
            var eachGrassPos = m_grassPosList[i];
            int xId = Mathf.Min(m_cellCountX -1 ,Mathf.FloorToInt((Mathf.InverseLerp(m_grassBoundsXMin, m_grassBoundsXMax, eachGrassPos.x) *m_cellCountX)));
            int zId = Mathf.Min(m_cellCountZ -1 ,Mathf.FloorToInt((Mathf.InverseLerp(m_grassBoundsZMin, m_grassBoundsZMax, eachGrassPos.z) *m_cellCountZ)));
            
            m_cellToPosList[xId + zId * m_cellCountX].Add(eachGrassPos);
        }
    
        // 将所有排序完之后的草放入到cell 列表冲 传入shader
        Vector3[] allGrassPosSortByCell = new Vector3[m_grassCnt];
        int offset = 0;
        for (int i = 0; i < m_cellToPosList.Length; i++) {
            for (int j = 0; j < m_cellToPosList[i].Count; j++)
            {
                allGrassPosSortByCell[offset] = m_cellToPosList[i][j];
                offset ++;
            }
        }

        m_grassAllPosBuffer.SetData(allGrassPosSortByCell);
        
        instanceMaterial.SetBuffer("_GrassAllPosBuffer", m_grassAllPosBuffer);
        instanceMaterial.SetBuffer("_GrassVisibleIDBuffer",m_grassVisibleIDBuffer);
        
        cullingComputeShader.SetBuffer(0,"_GrassVisibleIDBuffer", m_grassVisibleIDBuffer);
        cullingComputeShader.SetBuffer(0,"_GrassAllPosBuffer", m_grassAllPosBuffer);
        
    }

    
}
